/*
 * File      : fh_uart.c
 * This file is part of FH8620 BSP for RT-Thread distribution.
 *
 * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
 * All rights reserved
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Visit http://www.fullhan.com to get contact with Fullhan.
 *
 * Change Logs:
 * Date           Author       Notes
 */

/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
#include "inc/fh_driverlib.h"
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/

/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file here
 ***************************************************************************/

/******************************************************************************
 * Function prototype section
 * add prototypes for all functions called by this file,execepting those
 * declared in header file
 *****************************************************************************/

/*****************************************************************************
 * Global variables section - Exported
 * add declaration of global variables that will be exported here
 * e.g.
 *  int8_t foo;
 ****************************************************************************/

/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/

/* function body */

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

int uart_init(uart *port)
{
    port->UART_IER = 0;
    port->UART_LCR = 0;
    // port->UART_DLL = 0;
    // port->UART_DLH = 0;
    return 0;
}

UINT32 uart_get_status(uart *port) { return port->UART_USR; }
void uart_configure(uart *port, enum data_bits data_bit,
                    enum stop_bits stop_bit, enum parity parity,
                    UINT32 buard_rate, UINT32 uart_clk)
{
    UINT32 baud_div;
    UINT32 lcr_reg = 0;
    UINT32 ret;

    /*
    UINT32 divisor;
    UINT32 freq;
    divisor = DIV(buard_rate);
    port->UART_LCR |= UART_LCR_DLAB;
    port->UART_DLL = divisor & 0xFF;
    port->UART_DLH = (divisor >> 8) & 0xFF;
    port->UART_LCR &= ~UART_LCR_DLAB;*/

    do
    {
        // clear fifo...
        port->UART_FCR = UART_FCR_RFIFOR | UART_FCR_XFIFOR;
        // read status..
        ret = uart_get_status(port);
    } while (ret & UART_USR_BUSY);
    switch (data_bit)
    {
    case UART_DATA_BIT5:
        lcr_reg |= UART_LCR_DLS5;
        break;
    case UART_DATA_BIT6:
        lcr_reg |= UART_LCR_DLS6;
        break;
    case UART_DATA_BIT7:
        lcr_reg |= UART_LCR_DLS7;
        break;
    case UART_DATA_BIT8:
        lcr_reg |= UART_LCR_DLS8;
        break;
    default:
        lcr_reg |= UART_LCR_DLS8;
        break;
    }

    switch (stop_bit)
    {
    case UART_STOP_BIT1:
        lcr_reg |= UART_LCR_STOP1;
        break;
    case UART_STOP_BIT2:
        lcr_reg |= UART_LCR_STOP2;
        break;
    default:
        lcr_reg |= UART_LCR_STOP1;
        break;
    }

    switch (parity)
    {
    case UART_PARITY_EVEN:
        lcr_reg |= UART_LCR_EVEN | UART_LCR_PEN;
        break;
    case UART_PARITY_ODD:
        lcr_reg |= UART_LCR_PEN;
        break;
    case UART_PARITY_ST:
        lcr_reg |= UART_LCR_SP;
        break;
    case UART_PARITY_NONE:
    default:
        break;
    }

    switch (buard_rate)
    {
    case 115200:
        baud_div = BAUDRATE_115200;
        break;
    case 57600:
        baud_div = BAUDRATE_57600;
        break;
    case 38400:
        baud_div = BAUDRATE_38400;
        break;
    case 19200:
        baud_div = BAUDRATE_19200;
        break;
    case 9600:
        baud_div = BAUDRATE_9600;
        break;
    default:
        baud_div = BAUDRATE_115200;
        break;
    }

    // clear fifo
    port->UART_FCR = UART_FCR_RFIFOR | UART_FCR_XFIFOR;

    // div
    ret = port->UART_LCR;
    ret |= UART_LCR_DLAB;
    port->UART_LCR  = ret;
    port->RBRTHRDLL = baud_div & 0x00ff;
    port->DLHIER    = (baud_div & 0x00ff) >> 8;
    /* clear DLAB */
    ret            = ret & 0x7f;
    port->UART_LCR = ret;

    // line control
    port->UART_LCR = lcr_reg;
    // fifo control
    port->UART_FCR = UART_FCR_FIFOE | UART_FCR_RFIFOR | UART_FCR_XFIFOR |
                     UART_FCR_TET_1_4 | UART_FCR_RT_ONE;
}

int uart_enable_irq(uart *port, UINT32 mode)
{
    unsigned int ret;
    ret = port->UART_IER;
    ret |= mode;
    port->UART_IER = ret;
    return 0;
}

int uart_disable_irq(uart *port, UINT32 mode)
{
    unsigned int ret;
    ret = port->UART_IER;
    ret &= ~mode;

    port->UART_IER = ret;
    return 0;
}

UINT32 uart_get_iir_status(uart *port) { return port->UART_IIR; }
UINT32 uart_get_line_status(uart *port) { return port->UART_LSR; }
UINT32 uart_is_rx_ready(uart *port) { return port->UART_LSR & UART_LSR_DR; }
UINT8 uart_getc(uart *port) { return port->UART_RBR & 0xFF; }
void uart_putc(uart *port, UINT8 c)
{
    // while(!(port->UART_USR & UART_USR_TFNF));
    port->UART_THR = c;
}

void uart_set_fifo_mode(uart *port, UINT32 fifo_mode)
{
    port->UART_FCR = fifo_mode;
}
